#include "FactoryCore.hh"
#include "Stopwatch.hh"
#include <sys/time.h>
#include <unistd.h>

FactoryCore::FactoryCore(BlockingQueue* AssignedQueue, Group* AssignedGroup)
{
	State = false;
	
	QueueOfTask = AssignedQueue;
	checkNullFV(QueueOfTask, "QueueOfTask is NULL.");
	
	InfoOfFactory = AssignedGroup;
	checkNullFV(InfoOfFactory, "InfoOfFactory is NULL.");
	
	Running = false;
	TIdOfWatchdog = 0;
	IntervalOfWatch = 1000000;
	
	State = true;
}

ResultOf FactoryCore::run(USNumber Min, USNumber Max)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (Running)
		return RETURN_OK;
	
	Running = true;
	
	ResultOf Result;
	Result = startWatchdog();
	checkErrorWOR(Result, "Fail startWatchdog.");
	
	Result = adjustNumOfWorker_(Min, Max);
	checkErrorWR(Result, "Fail adjustNumOfWorker_.");
	
	if (InfoOfFactory->ListOfWorker->tellSize() < Min)
		notifyError(InfoOfFactory->ListOfWorker->tellSize(), 
			"The number of threads is not enough.");
	
	Result = RunTime.start();
	checkErrorWOR(Result, "Fail RunTime.start.");
	
	return RETURN_OK;
}

ResultOf FactoryCore::stop()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (!Running)
		return RETURN_OK;
	
	Running = false;
	
	ResultOf Result;
	Result = InfoOfFactory->setMin(0);
	checkErrorWR(Result, "Fail InfoOfFactory->setMin.");
	
	Result = QueueOfTask->flush();
	checkErrorWR(Result, "Fail QueueOfTask->flush.");
	
	Result = RunTime.stop();
	checkErrorWOR(Result, "Fail RunTime.stop.");
	
	Result = stopWatchdog();
	checkErrorWR(Result, "Fail stopWatchdog.");
	
	while (InfoOfFactory->ListOfWorker->tellSize() > 0)
	{
		Result = QueueOfTask->release();
		checkErrorWR(Result, "Fail QueueOfTask->release.");
	}
	
	return RETURN_OK;
}

ResultOf FactoryCore::adjustNumOfWorker(USNumber Min, USNumber Max)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (!Running)
		return RETURN_ERROR;
	
	ResultOf Result;
	Result= adjustNumOfWorker_(Min, Max);
	checkErrorWR(Result, "Fail adjustNumOfWorker_.");
	
	return RETURN_OK;
}

ResultOf FactoryCore::fetchTask(Task* NewTask)
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return RETURN_ERROR;
	}
	
	if (InfoOfFactory->tellAvailable() == 0)
		employ();
	
	ResultOf Result;
	Result = QueueOfTask->enqueue((void*)NewTask);
	checkErrorWR(Result, "Fail QueueOfTask->enqueue.");
	
	// NOTE
	// 블럭킹 가능한 매체를 통해서 태스크를 받는다면 태스크는 삽입과 동시에 처리될 가능성이 높다.
	// 하지만 실제로 그렇지 않기 때문에 블럭킹 기능을 usleep 함수로 시뮬레이션한다.
	
	// Result = pthread_yield();
	// checkErrorWR(Result, "Fail pthread_yield.");
	
	usleep(0);
	return RETURN_OK;
}

ResultOf FactoryCore::setIntervalOfWatchdog(USNumber Interval)
{
	IntervalOfWatch = Interval;
	return RETURN_OK;
}

USNumber FactoryCore::tellNumOfEmployee()
{
	if (!State)
	{
		fprintf(stderr, "This instance(%x) is not available.\n", (Number)this);
		return 0;
	}
	
	return InfoOfFactory->ListOfWorker->tellSize();
}

ResultOf FactoryCore::adjustNumOfWorker_(USNumber Min, USNumber Max)
{
	if (Min > Max)
	{
		notifyError(Max - Min, "Bad parameter(Max - Min).");
		return RETURN_PARAM_ERROR;
	}
	
	ResultOf Result;
	Result = InfoOfFactory->setMin(Min);
	checkErrorWR(Result, "Fail InfoOfFactory->setMin.");
	
	Result = InfoOfFactory->setMax(Max);
	checkErrorWR(Result, "Fail InfoOfFactory->setMax.");
	
	Number InsufficientNumOfWorker;
	InsufficientNumOfWorker = (Number)Min - (Number)InfoOfFactory->ListOfWorker->tellSize();
	for (Number LC = 0; LC < InsufficientNumOfWorker; LC++) 
		employ();
	
	Number RemainedNumOfWorker;
	RemainedNumOfWorker = (Number)InfoOfFactory->ListOfWorker->tellSize() - (Number)Max; 
	for (Number LC = 0; LC < RemainedNumOfWorker; LC++)
		fire();
	
	return RETURN_OK;
}

ResultOf FactoryCore::startWatchdog()
{
	ResultOf Result;
	Result = pthread_create(&TIdOfWatchdog, NULL, ThreadFunc, (void*)this);
	checkErrorWR(Result, "Fail pthread_create.");
	
	return RETURN_OK;
}

ResultOf FactoryCore::stopWatchdog()
{
	ResultOf Result;
	Result = pthread_join(TIdOfWatchdog, NULL);
	checkErrorWR(Result, "Fail pthread_join.");
	
	printf("Total Process Time = %d\n", 
		RunTime.tellMicroSec());
	
	return RETURN_OK;
}

void* FactoryCore::ThreadFunc(void* Param)
{
	FactoryCore* This;
	This = (FactoryCore*)Param;
	
	ResultOf Result;
	Result = This->watch();
	checkErrorWRNull(Result, "Fail This->watch.");
	
	return NULL;
}

ResultOf FactoryCore::watch()
{
	ResultOf Result;
	Stopwatch Timer;
	Number TimeSpend;
	Number Delay;
	
	USNumber NumOfTask;
	NumOfTask = 0;
	
	while (Running)
	{
		Result = Timer.start();
		checkErrorWR(Result, "Fail Timer.start.");
		
		// TEST
// 		printf("Worker = %d(%d / %d)\nTask = %d(%d)\n", 
// 			InfoOfFactory->ListOfWorker->tellSize(),
// 			InfoOfFactory->tellWorking(),
// 			InfoOfFactory->tellAvailable(),
// 			InfoOfFactory->tellNumOfCompletedTasks() - NumOfTask ,
// 			QueueOfTask->tellSize());
		
// 		NumOfTask = InfoOfFactory->tellNumOfCompletedTasks();
		
		Result = hookWatchdog();
		checkErrorWOR(Result, "Fail hookWatchdog.");
		
		Result = Timer.stop();
		checkErrorWR(Result, "Fail Timer.stop.");
		
		TimeSpend = Timer.tellMicroSec();
		Delay = IntervalOfWatch - TimeSpend;
		
		if (Delay > 0)
			usleep(Delay);
		
		// NOTE
		// usleep을 사용하지만, 이후에 signal 클래스로 변경하고
		// Delay 만큼 타임아웃을 걸고, 펙토리가 종료할 때 시그널을
		// 날리는 방식으로 고치는게 좋을 것 같다.
		
		// TEST
// 		printf("\n");
	}
	
	return RETURN_OK;
}

ResultOf FactoryCore::hookWatchdog()
{
	return RETURN_OK;
}

ResultOf FactoryCore::employ()
{
	if (InfoOfFactory->tellMax() <= InfoOfFactory->ListOfWorker->tellSize())
		return RETURN_NOT_OK;
	
	GroupWorker* NewWorker;
	NewWorker = new GroupWorker(QueueOfTask, InfoOfFactory);
	checkNullWR(NewWorker, "NewWorker is NULL.");
	
	ResultOf Result;
	Result = NewWorker->work();
	if (checkError(Result))
	{
		notifyError(Result, "Fail NewWorker->work.");
		delete NewWorker;
		return RETURN_ERROR;
	}
	
	return RETURN_OK;
}

ResultOf FactoryCore::fire()
{
	GroupWorker* SomeWorker;
	SomeWorker = (GroupWorker*)InfoOfFactory->ListOfWorker->ban();
	if (SomeWorker == NULL)
		return RETURN_OK;
	
	ResultOf Result;
	Result = fire(SomeWorker);
	checkErrorWR(Result, "Fail fire.");
	
	return RETURN_OK;
}

ResultOf FactoryCore::fire(GroupWorker* Worker)
{
	ResultOf Result;
	Result = Worker->retire();
	checkErrorWR(Result, "Fail Worker->retire.");
	
	return RETURN_OK;
}
